<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>贪吃蛇</title>
    <link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<div class="content">
    <h1>网页版贪吃蛇</h1>
    <p class="count">分数：<span id="num">0</span></p>
    <button id="start-game">开始游戏</button>
    <button id="end-game">结束游戏</button>
    <div id="map"></div>
</div>
<script>
    // 地图类
    class Map{
        constructor(el, rect = 10) {
            this.el = el
            this.rect = rect
            this.data = []
            this.rows = Math.ceil(Map.getStyle(el,'height') / rect)
            this.cells = Math.ceil(Map.getStyle(el,'width') / rect)
            Map.setStyle(el,'height',this.rows*rect)
            Map.setStyle(el,'width',this.cells*rect)
        }
        static getStyle(el,attr){
            return parseFloat(getComputedStyle(el)[attr])
        }
        static setStyle(el,attr,val){
            return el.style[attr] = val + 'px'
        }

        // 通过setData，往面板里面添加数据
        setData(newData){
            this.data = this.data.concat(newData)
        }
        clearData(){
            this.data.length = 0
        }
        include({x,y}){
            return !!this.data.find(item=> item.x === x && item.y === y)
        }
        render(){
            this.el.innerHTML = this.data.map(item=>{
                return `<span style="position: absolute;left: ${item.x*this.rect}px;top: ${item.y*this.rect}px;
                            width: ${this.rect}px;height: ${this.rect}px;background-color: ${item.color}"></span>`
            }).join("")
        }
    }
    // 食物类
    class Food{
        constructor(map, colors = ["red","pink","yellow","blue"]) {
            this.map = map
            this.colors = colors
            this.data = null
            this.create()
        }
        create(){
            let x = Math.floor(Math.random()*this.map.cells)
            let y = Math.floor(Math.random()*this.map.rows)
            let color = this.colors[Math.ceil(Math.random()*this.colors.length-1)]
            this.data = {x,y,color}
            console.log(this.data)
            // 如果重合，就重新生成一个点
            if(this.map.include(this.data)){
                this.create()
            }
            this.map.setData(this.data)
        }
    }

    // snake类
    class Snake{
        constructor(map,food) {
            this.data = [
                {
                    x:6,
                    y:4,
                    color:'green'
                },
                {
                    x:5,
                    y:4,
                    color:'#fff'
                },
                {
                    x:4,
                    y:4,
                    color:'#fff'
                },
                {
                    x:3,
                    y:4,
                    color:'#fff'
                },
                {
                    x:2,
                    y:4,
                    color:'#fff'
                }
            ]
            this.map = map
            this.food = food
            this.map.setData(this.data)
            this.direction = 'right'
        }
        move(){
            let i = this.data.length-1
            this.lastData = {
                x:this.data[i].x,
                y:this.data[i].y,
                color:this.data[i].color
            }
            // 修改为上一个格子的位置
            for (i;i>0;i--){
                this.data[i].x = this.data[i-1].x
                this.data[i].y = this.data[i-1].y
            }
            switch (this.direction){
                case "left":
                    this.data[0].x--
                    break
                case "right":
                    this.data[0].x++
                    break
                case "top":
                    this.data[0].y--
                    break
                case "bottom":
                    this.data[0].y++
            }
        }
        changeDir(dir){
            // 如果蛇此时正在上下移动，那我们就只能修改蛇让它左右移动
            if(this.direction === 'right' || this.direction === 'left'){
                if(dir === 'right'|| dir === 'left'){
                    return false // 修改方向失败
                }
            }else {
                // 如果蛇此时正在左右移动，那我们就只能修改蛇让它上下移动
                if(dir === 'top'|| dir === 'bottom'){
                    return false // 修改方向失败
                }
            }
            this.direction = dir
            return true

        };
        eatFood(){
            this.data.push(this.lastData)
            this.map.setData(this.lastData)
        }
    }

    // 游戏控制类
    class Game{
        constructor(el,rect) {
            this.map = new Map(el,rect)
            this.food = new Food(this.map)
            this.snake = new Snake(this.map,this.food)
            this.grade = 0
            this.map.render()
            this.timer = 0
            this.interval = 200
            this.keyDown = this.keyDown.bind(this)
            this.control()
        }
        // 开始游戏
        start(){
            this.move()
        }
        // 暂停游戏
        stop(){
            clearInterval(this.timer)
        }
        // 控制移动
        move(){
            this.stop()
            this.timer = setInterval(()=>{
                this.snake.move()
                this.map.clearData();
                this.map.setData(this.snake.data)
                this.map.setData(this.food.data)
                // 吃到食物
                if(this.isEat()){
                    this.grade++
                    this.snake.eatFood()
                    this.food.create()
                    document.getElementById('num').innerHTML = this.grade
                    this.interval=this.interval*0.9
                    this.stop()
                    this.start()
                }
                if(this.grade>=20){
                    alert("太棒了，你完成了游戏")
                    this.over()
                    return
                }
                if(this.isOver()){
                    alert("结束游戏")
                    this.over()
                    return
                }
                this.map.render()
            },this.interval)
        }
        // 是否结束游戏
        isOver(){
            // 情况1：蛇出了地图
            if(this.snake.data[0].x < 0 || this.snake.data[0].x>=this.map.cells || this.snake.data[0].y<0 ||this.snake.data[0].y>=this.map.rows){
                return true
            }
            // 情况2：蛇头碰到了自己
            for (let i=1;i<this.snake.data.length;i++){
                if(this.snake.data[i].x === this.snake.data[0].x &&
                    this.snake.data[i].y === this.snake.data[0].y){
                    return true
                }
            }
            return false

        }
        // 是否迟到食物
        isEat(){
            return this.snake.data[0].x === this.food.data.x && this.snake.data[0].y === this.food.data.y;
        }
        // 结束游戏
        over(){
            this.stop()
        }
        // 键盘摁下事件
        keyDown({keyCode}){
            switch (keyCode){
                case 39:
                    this.snake.changeDir('right')
                    break
                case 37:
                    this.snake.changeDir('left')
                    break
                case 40:
                    this.snake.changeDir('bottom')
                    break
                case 38:
                    this.snake.changeDir('top')
            }
        }
        // control控制器
        control(){
            window.addEventListener('keydown',this.keyDown)
        }
    }
    {
        let map = document.getElementById('map')
        let game = new Game(map,10)
        document.getElementById('start-game').onclick = function (){
            game.start()
        }
        document.getElementById('end-game').onclick = function (){
            game.stop()
        }
    }
</script>
</body>
</html>
